package com.dyz.wsw.aspect;


import com.dyz.wsw.annotation.Auth;
import com.dyz.wsw.common.consts.web.HttpConst;
import com.dyz.wsw.common.ex.NoPrincipalException;
import com.dyz.wsw.common.ex.NoWebExchangeException;
import com.dyz.wsw.common.pojo.auth.Principal;
import com.dyz.wsw.domain.admin.repository.cache.IUserCacheRepository;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;


/**
 * 权限校验用切面
 * @author dyz
 * @date 2025/1/12 23:44
 */
@Aspect
@Configuration
@Slf4j
public class AuthAspect implements HttpConst {


    private final IUserCacheRepository iUserCacheRepository;


    public AuthAspect(IUserCacheRepository iUserCacheRepository){
        this.iUserCacheRepository = iUserCacheRepository;
    }

    /**
     * 这样写将切入点和通知方法结合在一起，不在需要单独定义@Pointcut
     * 这里利用Spring AOP的参数绑定特性，当切面方法的参数里声明了对应注解类型（这里为Auth）
     * Spring会自动把目标方法上匹配的注解实例注入进来，这样代码更为简洁，减少了复杂的反射操作
     * @param joinPoint
     * @param auth
     * @return
     */
    @Around("@annotation(auth)")
    public Object aroundAuth(ProceedingJoinPoint joinPoint,Auth auth) {
        Object[] args = joinPoint.getArgs();
        ServerWebExchange exchange = getServerWebExchange(args);
        if (exchange==null){
            String message = joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
            throw new NoWebExchangeException(message);
        }

        Principal principal = (Principal)exchange.getAttributes().get(HEADER_AUTHORIZATION);

        // 权限校验
        checkPermission(principal,auth.value());

        log.debug("中断执行!");
        return null;
    }



    /**
     * 校验权限
     * @param principal 当事人
     * @param permission 权限字符
     * @return 校验结果
     */
    private boolean checkPermission(Principal principal,String permission){
        log.debug("当事人: {}",principal);
        if (principal==null){
            throw new NoPrincipalException("principal is null");
        }
        log.debug("正在校验权限: {}", permission);
        Long id = principal.getId();
        log.debug("当前用户id: {}",id);
        //1.根据id从缓存中读取用户持有的权限
//        iUserCacheRepository.getUserState(id)

        return true;
    }



    /**
     * 从方法的参数列表中拿取ServerWebExchange
     * 这样做的优缺点如下：
     * 1.缺点：需要在方法中的参数中添加ServerWebExchange让Spring自动完成参数绑定
     * 2.优点：无需关心绑定等过程，一切交给框架处理
     * @param args 方法的参数
     * @return 本次请求对象
     */
    private ServerWebExchange getServerWebExchange(Object[] args){
        for (Object arg : args) {
            if(arg instanceof ServerWebExchange){
                return (ServerWebExchange) arg;
            }
        }
        return null;
    }


}
